﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using haptic.src.engine.core;

namespace haptic.src.engine.animation
{
    public interface IKey
    {
        Object GetValue();
    }

    public class Key<T> : IKey
    {
        public Object GetValue() { return m_oValue; }
        public float m_fTime = 0.0f;
        public T m_oValue;
    }

    public class BezierKey<T> : Key<T>
    {
        public T m_vLeftTangent;
        public T m_vRightTangent;
    }

    public interface IInterpolator
    {
        Object Interpolate(IKey oK1, IKey oK2, float fRatio);
        bool CheckType(Type oRequestedType);
    }

    public class LinearFloatInterpolator : IInterpolator
    {
        public Object Interpolate(IKey oK1, IKey oK2, float fRatio)
        {
            return (float)oK1.GetValue() + ((float)oK2.GetValue() - (float)oK1.GetValue()) * fRatio;
        }
        public bool CheckType(Type oRequestedType)
        {
            return oRequestedType == typeof(float);
        }
    }
    public class LinearVector2Interpolator : IInterpolator
    {
        public Object Interpolate(IKey oK1, IKey oK2, float fRatio)
        {
            return (Vector2)oK1.GetValue() + ((Vector2)oK2.GetValue() - (Vector2)oK1.GetValue()) * fRatio;
        }
        public bool CheckType(Type oRequestedType)
        {
            return oRequestedType == typeof(Vector2);
        }
    }
    /*public class BezierVector2Interpolator : IInterpolator
    {
        public Object Interpolate(IKey oK1, IKey oK2, float fRatio)
        {
            return null;
        }
    }*/

    public class KeyList<T, I> where I : IInterpolator, new()
    {
        public KeyList()
        {
            if (!m_oInterpolator.CheckType(typeof(T)))
            {
                Log.Error("Invalid interpolator type used");
            }
        }
        public int FindKeyLoop(int iCached, float fTime, out float fRatio)
        {
            Debug.Assert(iCached < m_oKeys.Count);
            int iInd = iCached;
            float fDuration = m_oKeys[m_oKeys.Count - 1].m_fTime;
            if (fDuration == 0.0f)
            {
                fRatio = 0.0f;
                return 0;
            }
            float fReducedTime = fTime - (float)(int)(fTime / fDuration);

            if (m_oKeys[iInd].m_fTime > fReducedTime)
                iInd = 0;
            for (; iInd < m_oKeys.Count; iInd++)
                if (m_oKeys[iInd].m_fTime > fReducedTime)
                    break;
            iInd--;
            Debug.Assert(iInd >= 0);
            Debug.Assert(iInd < m_oKeys.Count-1);

            fRatio = fReducedTime - m_oKeys[iInd].m_fTime / (m_oKeys[iInd + 1].m_fTime - m_oKeys[iInd].m_fTime);
            return iInd;
        }

        public T GetValue(int iCachedIndex, float fTime, out int iNewCacheIndex)
        {
            float fRatio;
            iNewCacheIndex = FindKeyLoop(iCachedIndex, fTime, out fRatio);
            int iInd2 = iNewCacheIndex + 1;
            Key<T> oKey1 = m_oKeys[iNewCacheIndex];
            Key<T> oKey2 = m_oKeys[iInd2];
            return (T)m_oInterpolator.Interpolate(oKey1, oKey2, fRatio);
        }

        #region Attributes
        public List<Key<T>> m_oKeys = new List<Key<T>>();
        public I m_oInterpolator = new I();
        #endregion
    }
}
